package UMC.Web;

import UMC.Data.Utility;
import UMC.Security.AccessToken;
import UMC.Security.Identity;

import java.io.IOException;
import java.io.Writer;
import java.lang.reflect.Array;
import java.net.URI;
import java.net.URL;
import java.util.*;

public class WebClient {
    boolean isForm;

    interface IWebClient {
        Writer outputWriter() throws IOException;

        void redirect(String uri) throws IOException;
    }

    Map<String, Object> _items = new HashMap<>();
    final static int OuterDataEvent = 131072, Prompt = 2048;

    // InputStream InputStream;
    public Boolean IsVerify;

    public void clear(int Event) {
        if ((this.ClientEvent & Event) == Event) {
            this.ClientEvent = this.ClientEvent ^ Event;
        }

    }

    Map<String, Object> OuterHeaders = new HashMap<>();

    int ClientEvent;

    int RedirectTimes = 0;

    URI Uri;
    String UserHostAddress;
    URI UrlReferrer;

    String UserAgent;

    boolean isApp;

    boolean isCashier;
    AccessToken accessToken;

    public int getRedirectTimes() {
        return RedirectTimes;
    }

    public WebClient(URI uri, URI referrer, String UserAgent, String ip, AccessToken token, int redirectTimes) {

        this.Uri = uri;
        this.UserHostAddress = ip;
        this.UserAgent = UserAgent;
        this.UrlReferrer = referrer;
        this.accessToken = token;
        this.isCashier = token.isInRole(AccessToken.UserRole);
        if (!Utility.isEmpty(UserAgent))
            this.isApp = UserAgent.contains("UMC Client");
        this.RedirectTimes = redirectTimes;

    }

    void redirect(String model, String cmd, WebMeta meta) {
        if (!verify(model, cmd)) {
            return;
        }
        WebMeta header = new WebMeta();
        if (meta != null)
            header.put(model, meta);
        this.send(model, cmd, header);
    }

    public void command(String model, String cmd, String value) {

        if (!verify(model, cmd)) {
            return;
        }
        redirect(model, cmd, value);

    }

    void redirect(String model, String cmd, String value) {
        WebMeta header = new WebMeta();
        if (!Utility.isEmpty(value)) {

            header.put(cmd, value);
        }
        this.send(model, cmd, header);

    }

    boolean verify(String model, String cmd) {
        if (this.IsVerify == null) {

            Identity user = this.accessToken.identity();
            String key = String.format("%s.%s", model, cmd);
            WebAuthType authorizationType = WebAuthType.all;
            if (WebRuntime.authKeys.containsKey(key)) {
                authorizationType = WebRuntime.authKeys.get(key);
            } else if (WebRuntime.authKeys.containsKey(model)) {
                authorizationType = WebRuntime.authKeys.get(model);
            }
            if (authorizationType == WebAuthType.all) {
                this.IsVerify = true;
                return true;
            } else if (authorizationType == WebAuthType.user) {
                if (user.isInRole(AccessToken.UserRole)) {
                    this.IsVerify = true;
                    return true;
                }

            } else if (authorizationType == WebAuthType.userCheck) {
                if (user.isInRole(AccessToken.AdminRole)) {
                    this.IsVerify = true;
                    return true;
                } else if (user.isInRole(AccessToken.UserRole)) {
                    if (accessToken.authorization(key)) {
                        this.IsVerify = true;
                        return true;

                    }
                }

            } else if (authorizationType == WebAuthType.check) {
                if (user.isInRole(AccessToken.AdminRole)) {
                    this.IsVerify = true;
                    return true;
                } else if (user.isAuthenticated()) {
                    if (accessToken.authorization(key)) {
                        this.IsVerify = true;
                        return true;
                    }
                }
            } else if (authorizationType == WebAuthType.admin) {
                if (user.isInRole(AccessToken.AdminRole)) {
                    this.IsVerify = true;
                    return true;
                }

            } else if (authorizationType == WebAuthType.guest) {
                if (user.isAuthenticated()) {
                    this.IsVerify = true;
                    return true;
                } else {

                    this.OuterHeaders = new HashMap<>();
                    this.ClientEvent = WebEvent.PROMPT | WebEvent.DATAEVENT;

                    this.OuterHeaders.put("Prompt", new WebMeta().put("Title", "提示", "Text", "您没有登录，请登录"));

                    this.OuterHeaders.put("DataEvent", new WebMeta().put("type", "Login"));

                    return false;
                }
            } else {

                this.IsVerify = true;
                return true;
            }

            this.ClientEvent = WebEvent.PROMPT | WebEvent.DATAEVENT;

            this.OuterHeaders.put("Prompt", new WebMeta().put("Title", "提示", "Text", "您没有登录或权限受限"));

            this.OuterHeaders.put("DataEvent", new WebMeta().put("type", "Close"));
            ;
            return false;
        }
        return Utility.isNull(this.IsVerify, false);

    }

    public void command(String model, String cmd, Map<String, String> QueryString) {
        if (!verify(model, cmd)) {
            return;
        }
        redirect(model, cmd, QueryString);

    }

    void redirect(String model, String cmd, Map<String, String> QueryString) {

        switch (QueryString.size()) {
            case 0:
                this.redirect(model, cmd, "");
                break;
            case 1:
                Map.Entry<String, String> entry = QueryString.entrySet().stream().findFirst().get();
                if (Utility.isEmpty(entry.getValue())) {
                    this.redirect(model, cmd, entry.getKey());
                    return;
                } else if ("_".equals(entry.getKey())) {

                    this.redirect(model, cmd, entry.getValue());
                    return;
                }
            default:
                String singleValue = "";
                Map<String, String> hash = new Hashtable<>();

                for (String key : QueryString.keySet()) {
                    String value = QueryString.get(key);
                    if (Utility.isEmpty(value) || "_".equals(key)) {
                        singleValue = key;

                    } else {
                        hash.put(key, value);
                    }
                }
                WebMeta header = new WebMeta();
                header.put(model, hash);
                if (!Utility.isEmpty(singleValue)) {
                    header.put(cmd, singleValue);
                }

                this.send(model, cmd, header);
                break;
        }

    }

    void outputHeader(WebMeta header) {

        Map<String, Object> dic = header.map();

        for (String key : dic.keySet()) {
            Object value = dic.get(key);
            if (key.equalsIgnoreCase("DataEvent")) {
                if (this.OuterHeaders.containsKey(key)) {
                    List<Object> ats = new LinkedList<>();
                    Object ts = this.OuterHeaders.get(key);

                    if (ts.getClass().isArray()) {
                        for (int i = 0, l = Array.getLength(ts); i < l; i++) {
                            ats.add(Array.get(ts, i));
                        }
                    } else {
                        ats.add(ts);
                    }
                    if (value.getClass().isArray()) {
                        for (int i = 0, l = Array.getLength(ts); i < l; i++) {
                            ats.add(Array.get(ts, i));
                        }
                    } else {

                        ats.add(ts);

                    }
                    this.OuterHeaders.put(key, ats.toArray());
                } else {
                    this.OuterHeaders.put(key, value);
                }
            } else {
                this.OuterHeaders.put(key, value);
            }
        }

    }

    private WebContext context;

    void send(String model, String cmd, WebMeta doc) {
        context = WebRuntime.processRequest(model, cmd, doc, this);
        WebResponse response = context.response();
        WebRequest request = context.request();
        this.RedirectTimes++;
        int clientEvent = response.ClientEvent;

        if ((clientEvent & OuterDataEvent) == OuterDataEvent) {

            this.ClientEvent = clientEvent;
            this.outputHeader(response.Headers);
            return;
        }
        if ((clientEvent & WebEvent.ASYNCDIALOG) == WebEvent.ASYNCDIALOG) {

            if (!Utility.isEmpty(response._model)) {

                if ((clientEvent & WebClient.Prompt) != WebClient.Prompt) {
                    response._model = response._cmd = null;
                }
            }
        }
        if (!Utility.isEmpty(response._model)) {

            if (this.RedirectTimes > 10) {
                throw new IllegalArgumentException("请求重定向超过最大次数");
            }

            if (clientEvent != 0) {

                this.ClientEvent |= clientEvent;
                outputHeader(response.Headers);
            }

            if (!Utility.isEmpty(response._value)) {

                if (response._value.startsWith("{")) {
                    Map<String, Object> p = (Map<String, Object>) UMC.Data.JSON.deserialize(response._value);// as
                    // Hashtable;
                    if (p != null) {
                        Map<String, String> pos = new HashMap<>();
                        for (Map.Entry<String, Object> key : p.entrySet()) {
                            pos.put(key.getKey(), String.valueOf(key.getValue()));
                        }
                        this.redirect(response._model, response._cmd, pos);
                    }
                } else if (response._value.contains("&")) {
                    Map<String, String> query = Utility.queryString(response._value);
                    this.redirect(response._model, request._cmd, query);
                } else {
                    this.redirect(response._model, response._cmd, response._value);
                }
            }
            return;
        }
        context.complete();
        outputHeader(response.Headers);
        this.ClientEvent = this.ClientEvent | clientEvent;

    }

    public static void register(Class cl) {
        WebRuntime.register(cl);
    }

    public void writeTo(IWebClient client) throws IOException {

        if (((this.ClientEvent) & OuterDataEvent) == OuterDataEvent) {
            Object data = this.OuterHeaders.get("Data");
            if (data instanceof WebClient) {
                WebClient d = (WebClient) data;
                d.writeTo(client);
                return;
            } else if (data instanceof URI || data instanceof URL) {

                String url = data.toString();

                if (this.isForm) {
                    this.ClientEvent = WebEvent.DATAEVENT;
                    this.OuterHeaders.clear();
                    this.OuterHeaders.put("DataEvent", new WebMeta().put("type", "Url", "value", url));
                } else {
                    client.redirect(url);
                    return;
                }

            } else {
                UMC.Data.JSON.serialize(data, client.outputWriter());
                return;
            }
        }
        Writer writer = client.outputWriter();
        writer.write("{\"ClientEvent\":");

        writer.write(this.ClientEvent + "");
        if (this.OuterHeaders != null && this.OuterHeaders.size() > 0) {

            writer.write(",\"Headers\":");
            UMC.Data.JSON.serialize(this.OuterHeaders, writer);
        }

        if (RedirectTimes > 1 && this.context != null && this.context.runtime._activity == null) {
            WebRequest request = this.context.request();
            WebMeta _Redirect = new WebMeta().put("model", request._model, "cmd", request._cmd);
            WebMeta sv = Utility.isNull(request.sendValues(), new WebMeta());
            String value = request.sendValue();
            if (!Utility.isEmpty(value)) {
                if (sv.size() > 0) {
                    sv.put("_", value);
                    _Redirect.put("send", sv);
                } else {
                    _Redirect.put("send", value);
                }

            } else if (sv.size() > 0) {

                _Redirect.put("send", sv);
            }

            writer.write(",\"Redirect\":");
            UMC.Data.JSON.serialize(_Redirect, writer);
        }
        writer.write("}");

    }

}